<?php
/**
 * CPageCrud class file.
 * 
 * @author Lucas Vasconcelos <contato@lucasvasconcelos.com>
 * @copyright Copyright(c) 2010 Lucas Vasconcelos
 * @license BSD License http://www.lucasvasconcelos.com/yap/license
 * @link http://www.lucasvasconcelos.com/yap/
 * @version $Id: CPageCrud.php 20 2010-02-08 20:31:41Z contato@lucasvasconcelos.com $
 * @package yap.web.ui
 */

Prado::using('yap.web.ui.CPageGrid');
Prado::using('yap.web.ui.interfaces');
Prado::using('yap.web.ui.CCrudEventHandler');
Prado::using('yap.web.ui.CCrudManager');
Prado::using('yap.web.CActionType');
Prado::using('yap.helpers.CCtrlFinder');
Prado::using('yap.helpers.CDate');

/**
 * CPageCrud class.
 * 
 * Infraestrutura para construir páginas de cadastro.
 * 
 * Páginas derivadas de CPageCrud são capazes de realizar as tarefas de exibição, criação, atualização
 * e exclusão de registros (tarefas definidas em {@link CActionType}.
 * 
 * Uma CPageCrud possúi dois 'estados': Grid e Formulário.
 * O estado padrão é 'Grid' que oferece o mesmo comportamento de uma CPageGrid. No modo Formulário,
 * apresenta um formulário HTML para entrada de dados.
 * 
 * Para criar estes dois estados, é necessário adicionar dois TPanel, no template da página, cujos IDs
 * devem ser pn_grid e pn_form (definidos em {@link CPanelName}).
 * 
 * Os campos do formulário podem ser automaticamente populados e ter seus valores coletados e persistidos,
 * se o padrão de nomenclatura for seguido.
 * 
 * [PREFIXO][GRUPO]_[COLUNA DA TABELA]
 * 
 * Ex. f_user_username / f_user_email / f_user_create_time 
 * 
 * CPageCrud utiliza objetos TActiveRecord para executar as tarefas de persistência de dados.
 * 
 * @author Lucas Vasconcelos <contato@lucasvasconcelos.com>
 * @version 1.1 $Revision: 20 $
 * @package yap.web.ui
 */
abstract class CPageCrud extends CPageGrid implements IPageCrud
{
	/**
	 * @var CCrudManager
	 */
	private $_crudManager=null;
	
	/**
	 * @var TActiveRecord
	 */
	private $_record=null;
	
	/**
	 * Este método é invocado durante o estágio OnLoad, quando o usuário executa a ação 'CActionType::Create'.
	 * Este método dispara o evento 'OnCreate' para acionar os manipuladores de evento.
	 * Se sobreescrever este método, certifique-se de chamar a implementação da classe pai para que
	 * os manipuladores de evento sejam acionados.
	 * @param TEventParameter parâmetro do evento que deve ser passado para os manipuladores de evento.
	 */
	public function onCreate($param)
	{
		$this->raiseEvent('OnCreate',$this,$param);
	}
	
	/**
	 * Este método é invocado durante o estágio OnLoad, quando o usuário executa a ação 'CActionType::Read'.
	 * Este método dispara o evento 'OnRead' para acionar os manipuladores de evento.
	 * Se sobreescrever este método, certifique-se de chamar a implementação da classe pai para que
	 * os manipuladores de evento sejam acionados.
	 * @param TEventParameter parâmetro do evento que deve ser passado para os manipuladores de evento.
	 */
	public function onRead($param)
	{
		$this->raiseEvent('OnRead',$this,$param);
	}
	
	/**
	 * Este método é invocado durante o estágio OnLoad, quando o usuário executa a ação 'CActionType::Update'.
	 * Este método dispara o evento 'OnUpdate' para acionar os manipuladores de evento.
	 * Se sobreescrever este método, certifique-se de chamar a implementação da classe pai para que
	 * os manipuladores de evento sejam acionados.
	 * @param TEventParameter parâmetro do evento que deve ser passado para os manipuladores de evento.
	 */
	public function onUpdate($param)
	{
		$this->raiseEvent('OnUpdate',$this,$param);
	}
	
	/**
	 * Este método é invocado durante o estágio OnLoad, quando o usuário executa a ação 'CActionType::Delete'.
	 * Este método dispara o evento 'OnDelete' para acionar os manipuladores de evento.
	 * Se sobreescrever este método, certifique-se de chamar a implementação da classe pai para que
	 * os manipuladores de evento sejam acionados.
	 * @param TEventParameter parâmetro do evento que deve ser passado para os manipuladores de evento.
	 */
	public function onDelete($param)
	{
		$this->raiseEvent('OnDelete',$this,$param);
	}
	
	/**
	 * Este método é invocado durante o estágio OnLoad, quando o usuário executa a ação 'CActionType::View'.
	 * Este método dispara o evento 'OnView' para acionar os manipuladores de evento.
	 * Se sobreescrever este método, certifique-se de chamar a implementação da classe pai para que
	 * os manipuladores de evento sejam acionados.
	 * @param TEventParameter parâmetro do evento que deve ser passado para os manipuladores de evento.
	 */
	public function onView($param)
	{
		$this->raiseEvent('OnView',$this,$param);
	}
	
	/**
	 * Este método geralmente é invocado durante o OnClick de um botão, quando o usuário deseja persistir as
	 * informações do formulário.
	 * Este método dispara o evento 'OnSave' para acionar os manipuladores de evento.
	 * Se sobreescrever este método, certifique-se de chamar a implementação da classe pai para que
	 * os manipuladores de evento sejam acionados.
	 * @param TEventParameter parâmetro do evento que deve ser passado para os manipuladores de evento.
	 */
	public function onSave($param)
	{
		$this->raiseEvent('OnSave',$this,$param);
	}
	
	/**
	 * Este método geralmente é invocado durante o OnClick de um botão, quando o usuário deseja persistir as
	 * informações do formulário.
	 * Este método dispara o evento 'OnBindFields' para acionar os manipuladores de evento.
	 * Se sobreescrever este método, certifique-se de chamar a implementação da classe pai para que
	 * os manipuladores de evento sejam acionados.
	 * @param TEventParameter parâmetro do evento que deve ser passado para os manipuladores de evento.
	 */
	public function onBindFields($param)
	{
		$this->raiseEvent('OnBindFields',$this,$param);
	}
	
	/**
	 * Não implementado.
	 */
	public function setAction($value) { }
	
	/**
	 * Action deve ser um dos valores definidos em {@link CAtionType}.
	 * @return string valor do parâmetro 'a' passado durante a user request. {@link CActionType::Read} se nenhum parâmetro for informado.
	 */
	public function getAction()
	{
		return $this->getRequest()->contains('a') ? $this->getRequest()->itemAt('a') : CActionType::Read;
	}
	
	/**
	 * @return mixed valor do parâmetro 'id' passado durante a user request. null, se o parâmetro não foi definido.
	 */
	public function getRecordId()
	{
		return $this->getRequest()->contains('id') ? $this->getRequest()->itemAt('id') : null;
	}
	
	/**
	 * @return TActiveRecord instancia contendo dados do registro selecionado.
	 */
	public function getRecord()
	{
		if($this->_record===null)
			$this->_record=$this->getFinder()->findByPk($this->getRecordId());
		return $this->_record;
	}
	
	/**
	 * Implementação default.
	 * @see CPageGrid#getData($criteria)
	 */
	public function getData(TActiveRecordCriteria $criteria)
	{
		return $this->getFinder()->findAll($criteria);
	}
	
	/**
	 * @return CCrudManager
	 */
	public function getCrudManager()
	{
		if($this->_crudManager===null)
			$this->_crudManager=new CCrudManager($this);
		return $this->_crudManager;
	}
	
	/**
	 * Este método é chamado durante os eventos {@link onCreate OnCreate}, {@link onUpdate OnUpdate} e {@link onView OnView}
	 * Sobreescreva para adicionar sua própria rotina de inicialização.
	 */
	public function initForm() { }
	
	public function save($sender,$param)
	{
		$this->onSave(null);
	}
	
	public function beforeSave(TActiveRecord $record) { }
	
	public function afterSave(TActiveRecord $record) { }
	
	public function beforeDelete(TActiveRecord $record) { }
	
	public function afterDelete() { }
	
	public function constructUrl($action,$id=null)
	{
		TPropertyValue::ensureEnum($action, 'CActionType');
		return $this->getUrl().'&amp;a='.$action.(($id===null) ? '' : '&amp;id='.$id);
	}

	public function allowedActions()
	{
		return array(CActionType::Create, CActionType::Update, CActionType::Delete);
	}
	
	/**
	 * Registra manipuladores de evento para eventos de CPageCrud.
	 */
	protected function registerEventHandlers()
	{
		parent::registerEventHandlers();
		$this->attachEventHandler('OnCreate',array('CCrudEventHandler','create'));
		$this->attachEventHandler('OnRead',array('CCrudEventHandler','read'));
		$this->attachEventHandler('OnUpdate',array('CCrudEventHandler','update'));
		$this->attachEventHandler('OnDelete',array('CCrudEventHandler','delete'));
		$this->attachEventHandler('OnView',array('CCrudEventHandler','view'));
		$this->attachEventHandler('OnSave',array('CCrudEventHandler','save'));
		$this->attachEventHandler('OnBindFields',array('CCrudEventHandler','bindFields'));
	}
	
	/**
	 * Processa requisição do usuário.
	 * Sobreescreve implementação anterior. Para lhe dar com ações disponíveis para {@link CPageCrud}.
	 * @see CPageGrid#processRequest()
	 */
	protected function processRequest()
	{
		if(!$this->getIsPostBack())
		{
			switch($this->getAction())
			{
				case CActionType::Create:
					Prado::trace('PageCrud onCreate()','yap.web.ui');
					$this->onCreate(null); break;
				case CActionType::Update:
					Prado::trace('PageCrud onUpdate()','yap.web.ui');
					$this->onUpdate(null); break;
				case CActionType::Delete:
					Prado::trace('PageCrud onDelete()','yap.web.ui');
					$this->onDelete(null); break;
				case CActionType::View:
					Prado::trace('PageCrud onView()','yap.web.ui');
					$this->onView(null); break;
				case CActionType::Read:
				default:
					Prado::trace('PageCrud onRead()','yap.web.ui');
					$this->onRead(null); break;
			}
		}
		else
			Prado::trace('PageCrud postBack event.','yap.web.ui');
	}
}

class CPanelName extends TEnumerable
{
	const pn_grid='pn_grid';
	const pn_form='pn_form';
}